package com.listeningframework.boot.commons.string;

import java.io.Writer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.google.common.collect.Sets;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
 * 
 * XStream CDATA Tag Support
 *
 * @author lixining
 * @version $Id: XstreamXppDriver.java, v 0.1 2016年6月30日 上午10:43:49 lixining Exp $
 */
public class XstreamXppDriver extends XppDriver {
    /**
     * 检测类型
     */
    private CDATA_CHECK_TYPE         checkType     = CDATA_CHECK_TYPE.TYPE;
    /**
     * SUPPORT TYPE SET
     */
    private static final Set<String> SUPPORT_TYPES = Sets.newHashSet();
    /**
     * SUPPORT TYPE SET
     */
    private static final Set<String> SUPPORT_ATTRS = Sets.newHashSet();

    public XstreamXppDriver() {
        SUPPORT_TYPES.add(String.class.getName());
        SUPPORT_TYPES.add(Character.class.getName());
    }

    /**
     * Writer
     */
    @Override
    public HierarchicalStreamWriter createWriter(Writer out) {
        return new PrettyPrintWriter(out) {
            private String   attributeName = null;
            private Class<?> clazz         = null;

            @Override
            @SuppressWarnings("rawtypes")
            public void startNode(String name, Class clazz) {
                super.startNode(name, clazz);
                this.attributeName = name;
                this.clazz = clazz;
            }

            @Override
            protected void writeText(QuickWriter writer, String text) {
                if (isNeedCDATA(attributeName, text, clazz)) {
                    writer.write(cDATA(text));
                } else {
                    writer.write(text);
                }
            }

            private String cDATA(String text) {
                return "<![CDATA[" + text + "]]>";
            }
        };
    }

    /**
     * Is Need CDATA
     */
    private boolean isNeedCDATA(String name, String value, Class<?> clazz) {
        if (isByType(checkType)) {
            return SUPPORT_TYPES.contains(clazz.getName());
        }
        if (isByConfig(checkType)) {
            return SUPPORT_ATTRS.contains(name);
        }
        return false;
    }

    /**
     * Check Type Enumeration
     */
    private enum CDATA_CHECK_TYPE {
        NONE, TYPE, CONFIG;
    }

    /**
     * If Is Check By Type
     */
    private static boolean isByType(CDATA_CHECK_TYPE type) {
        return StringUtils.equals(CDATA_CHECK_TYPE.TYPE.name(), type.name());
    }

    /**
     * If Is Check By Config
     */
    private static boolean isByConfig(CDATA_CHECK_TYPE type) {
        return StringUtils.equals(CDATA_CHECK_TYPE.CONFIG.name(), type.name());
    }

    public void setCheckTypeNone() {
        this.checkType = CDATA_CHECK_TYPE.TYPE;
    }

    public void setCheckByType() {
        this.checkType = CDATA_CHECK_TYPE.TYPE;
    }

    public void setCheckByConfig() {
        this.checkType = CDATA_CHECK_TYPE.CONFIG;
    }

    public void addSupportTypes(Collection<String> c) {
        SUPPORT_TYPES.addAll(c);
    }

    public void addSupportAtts(Collection<String> c) {
        SUPPORT_ATTRS.addAll(c);
    }

    public void addSupportTypes(String... types) {
        SUPPORT_TYPES.addAll(Arrays.asList(types));
    }

    public void addSupportAtts(String... attrs) {
        SUPPORT_ATTRS.addAll(Arrays.asList(attrs));
    }
}
